#!/usr/python
ident = '$Id: contact_ui.py 132 2006-06-27 16:48:21Z trevor $'

# Contact Service
import sys, wx, common, images, re, rex, contacts, time, editors
from xml.sax 	import saxutils
from xml.dom	import minidom

# Sort BY and Find By
BY_ALL				= 0
BY_FN				= 1
BY_LN				= 2
BY_ID				= 3
BY_XID				= 4
BY_EMAIL			= 5
BY_COMPANY			= 6
BY_USERNAME			= 7
BY_PASSWORD			= 8
BY_GID				= 9
BY_SPID				= 10
BY_BPID				= 11
ID_CGTIMER_TIMER	= 102


####################################################################################################
# Controls
ID_RPP				= 101
ID_FINDBY_TEXT		= 102
ID_GET_BUTTON		= 103
ID_CONTACTS_LIST	= 104
ID_PAGER			= 105
ID_NEXT_BUTTON  	= 106
ID_PREV_BUTTON  	= 107
ID_SHOWBY			= 108
ID_GROUPS_LIST		= 109
ID_NB				= 110

ID_CONTACT_CREATE	= 201
ID_CONTACT_EDIT		= 202
ID_CONTACT_DELETE	= 203
ID_CONTACT_REMOVE	= 204
ID_CONTACT_COPY		= 205
ID_CONTACT_PASTE	= 206
ID_CONTACT_RECOVER	= 207
ID_CONTACT_EMPTY	= 208

ID_GROUP_CREATE		= 301
ID_GROUP_EDIT		= 302
ID_GROUP_DELETE		= 303
ID_GROUP_EXPUNGE	= 304
ID_GROUP_REMOVE		= 305
ID_GROUP_COPY		= 306
ID_GROUP_PASTE		= 307

ID_TRASH_ALL		= 401
ID_TRASH_SELECTED	= 402
ID_TRASH_DATE		= 403

ID_CONTACT_DESIGNATE= 207
ID_CONTACT_CLEAR_DESIGNATE= 208
ID_CONTACT_COPY_UUID= 209

class drop_target( wx.PyDropTarget ):
	def __init__( self, control, main ):
		wx.PyDropTarget.__init__( self )
		self.control = control
		self.data   = wx.PyTextDataObject()
		self.SetDataObject( self.data )
		self.main = main

	def OnEnter( self, x, y, d ):
		#print "OnEnter: %d %d %d\n" % (x, y, d)
		return wx.DragCopy

	def OnLeave( self ):
		#print "OnLeave"
		pass

	def OnDrop( self, x, y ):
		#print "OnDrop: %d %d\n" % ( x, y )
		return True

	def OnDragOver( self, x, y, d ):
		#print "OnDragOver: %d %d %d\n" % (x, y, d)
		return wx.DragCopy

	def OnData( self, x, y, d ):
		common.dprint( "OnData: %d %d %d\n" % (x, y, d) )
		cur = wx.BusyCursor()
		if self.GetData():
			# If we drop something on the main CG lists, it gets removed from wherever we got it
			if self.main: return wx.DragMove
			data = self.data.GetText()
			common.dprint( "Got dropped data --[%s]--" % data )
			#parse the data
			sdom  = None
			snode = None
			try:
				sdom 	= minidom.parseString( data )
				snode	= sdom.firstChild
			except:
				common.show_error( "Failed to parse dropped data", False )
				del sdom
				del snode
				return
			if isinstance( self.control, group_control ) and snode.nodeName == 'groups':
				# We are adding a group
				if self.control.account.window: self.control.account.window.SetStatusText( "Applying for membership ..." )
				wx.Yield()
				cdom = '<instructions>'
				grps = []
				for inode in snode.getElementsByTagName("group"):
					grp = rex.contacts.group( self.control.account, inode.toxml() )
					grps.append( grp )
					cdom = cdom + "<instruction action=\"%s\" group_id=\"%s\" contact_id=%s />" % \
						( "add", grp["id"], saxutils.quoteattr( self.control.rex_object["id"] ) )
				cdom = cdom + "</instructions>"
				try:
					result = self.control.account.execute( 'base', "memberships_addremove", DOM=cdom )
				except:
					common.show_error('Failed to modify membership')
					return None
				# Add to the list
				if len( grps ):
					cache = self.control.get_cache()
					self.control.clear_cache = True
					for grp in grps:
						if grp['memberships'].strip() == '': grp['memberships'] = 0
						grp['memberships'] = str( int( grp['memberships'] ) + 1 )
						self.control.AppendGroup( grp )
						cache.append( grp )
					self.control.resize_columns()
				if self.control.account.window: self.control.account.window.SetStatusText( "Ready" )
				return wx.DragCopy
			if isinstance( self.control, contact_control ) and snode.nodeName == 'contacts':
				# We are adding a contact
				if self.control.account.window: self.control.account.window.SetStatusText( "Applying for membership ..." )
				wx.Yield()
				cdom = '<instructions>'
				cons = []
				for inode in snode.getElementsByTagName("contact"):
					con = rex.contacts.contact( self.control.account, inode.toxml() )
					cons.append( con )
					cdom = cdom + "<instruction action=\"%s\" group_id=\"%s\" contact_id=%s />" % \
						( "add", self.control.rex_object["id"], saxutils.quoteattr( con["id"] ) )
				cdom = cdom + "</instructions>"
				try:
					result = self.control.account.execute( 'base', "memberships_addremove", DOM=cdom )
				except:
					common.show_error('Failed to modify membership')
					return None
				if len( cons ):
					cache = self.control.get_cache()
					self.control.clear_cache = True
				for con in cons:
					if len( con['memberships'] ) > 0: con['memberships'] = con['memberships'] + ","
					con['memberships'] = con['memberships'] + self.control.rex_object["id"]
					self.control.AppendContact( con )
					cache.append( con )
				self.control.resize_columns()
				if self.control.account.window: self.control.account.window.SetStatusText( "Ready" )
				return wx.DragCopy
		else:
			common.dprint("Failed to get the dnd data")
		return wx.DragNone



class contact_control( wx.Panel ):
	def __init__( self, parent, account, main, trash=False ):
		wx.Panel.__init__( self, parent, -1 )
		self.parent 	= parent
		self.account 	= account
		self.qualifier	= ""
		self.qid		= ""
		self.main		= main
		self.trash		= trash
		self.create_menu_items()
		self.account.window.designated_contact_uuid = self.account.designated
		# Connect a timer to this frame
		self.timer = wx.Timer( self, ID_CGTIMER_TIMER )
		self.Bind( wx.EVT_TIMER, self.OnTimer, id=ID_CGTIMER_TIMER )
		# Create the top bar
		self.topbar = wx.BoxSizer( wx.HORIZONTAL )
		if not self.trash and self.main:
			# The new button
			self.new_button = wx.BitmapButton( self, -1, images.getNewContactBitmap(), size=common.button_size, style=wx.NO_BORDER )
			self.new_button.Bind( wx.EVT_BUTTON, self.create_new )
			self.topbar.Add( self.new_button, 0 )
		# The refresh button
		self.refresh_button = wx.BitmapButton( self, -1, images.getRefreshBitmap(), size=common.button_size, style=wx.NO_BORDER )
		self.refresh_button.Bind( wx.EVT_BUTTON, self.OnRefresh )
		self.topbar.Add( self.refresh_button, 0 )
		# The show selector
		variables = ['', 'id','xid','uuid','first_name','last_name','email','username','password']
		for var in account.window.binder_control.contact_variables:
			if var not in variables: variables.append( var )
		self.variable_choice = wx.Choice( self, -1, choices=variables )
		self.variable_choice.SetSelection( 0 )
		self.variable_choice.Bind( wx.EVT_CHOICE, self.OnShowByChange )
		self.topbar.Add( self.variable_choice, 1, wx.EXPAND )
		# the relation button
		self.find_rel_button = wx.BitmapButton( self, -1, images.getEqualsBitmap(), size=common.button_size, style=wx.NO_BORDER )
		self.find_rel_button.Bind( wx.EVT_BUTTON, self.OnRelChange )
		self.find_rel_button.Enable( False )
		self.topbar.Add( self.find_rel_button, 0 )
		# The find text box
		self.find_text = wx.TextCtrl( self, -1, '', style=wx.TE_PROCESS_ENTER )
		self.find_text.Enable( False )
		self.find_text.Bind( wx.EVT_TEXT, self.OnFindTextKeyPress )
		self.find_text.Bind( wx.EVT_TEXT_ENTER, self.OnRefresh )
		self.topbar.Add( self.find_text, 2, wx.EXPAND )
		# The finding selector
		self.rpp_choice = wx.Choice( self, ID_RPP, choices=["10 per page", "30 per page","50 per page", "100 per page"] )
		self.rpp_choice.SetSelection( 1 )
		self.Bind( wx.EVT_CHOICE, self.OnRPPChange, id=ID_RPP )
		self.topbar.Add( self.rpp_choice, 1, wx.EXPAND )
		#self.find_by = 1	# Find by ID by default
		# zoom
		if main:
			self.zoom_button = wx.BitmapButton( self, -1, images.getZoomBitmap(), size=common.button_size, style=wx.NO_BORDER )
			self.zoom_button.Bind( wx.EVT_BUTTON, self.OnZoom )
			self.topbar.Add( self.zoom_button, 0 )
		# Enable the top bar controls
		self.reset_values()
		#self.enable_topbar( True )
		self.filter_set = False
		# Create the list
		self.list = wx.ListCtrl( self, ID_CONTACTS_LIST, style=wx.LC_REPORT )
		self.il = wx.ImageList(16, 16)
		# Load our images
		self.contact_icon 	= self.il.Add( images.getContactBitmap() )
		self.list.SetImageList( self.il, wx.IMAGE_LIST_SMALL )
		# Store the default item color
		self.item_color = self.list.GetTextColour()
		self.list.Bind( wx.EVT_LEFT_DCLICK, self.OnItemDoubleClick )
		# default?
		self.list.Bind( wx.EVT_RIGHT_DOWN, self.OnListRightClick )
		# for wxGTK
		#self.list.Bind( wx.EVT_RIGHT_UP, self.OnListRightClick )
		# for wxMSW
		#self.list.Bind( wx.EVT_COMMAND_RIGHT_CLICK, self.OnListRightClick )
		self.Bind( wx.EVT_LIST_ITEM_SELECTED, self.OnItemActivated, id=ID_CONTACTS_LIST )
		self.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, id=ID_CONTACTS_LIST )
		# Set the drop target
		dt = drop_target( self, self.main )
		self.list.SetDropTarget( dt )
		# Add it's columns
		info = wx.ListItem()
		info.m_mask = wx.LIST_MASK_TEXT # | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
		info.m_format = 0
		info.m_text = "Name"
		self.list.InsertColumnInfo(0, info)

		info.m_text = "Email"
		self.list.InsertColumnInfo(1, info)

		info.m_text = "ID"
		self.list.InsertColumnInfo(2, info)

		info.m_text = "XID"
		self.list.InsertColumnInfo(3, info)

		info.m_text = "Username"
		self.list.InsertColumnInfo(4, info)

		info.m_text = "Password"
		self.list.InsertColumnInfo(5, info)

		info.m_text = "Groups"
		self.list.InsertColumnInfo(6, info)

		info.m_text = "Subscribed"
		self.list.InsertColumnInfo(7, info)

		info.m_text = "Blocked"
		self.list.InsertColumnInfo(8, info)

		info.m_text = "Instances"
		self.list.InsertColumnInfo(9, info)

		info.m_text = "UUID"
		self.list.InsertColumnInfo(10, info)

		info.m_text = "Created On"
		self.list.InsertColumnInfo(11, info)

		if self.trash:
			info.m_text = "Trashed On"
			self.list.InsertColumnInfo(12, info)

		# Resize them all
		self.resize_columns()

		# Create the bottom bar
		self.botbar = wx.BoxSizer( wx.HORIZONTAL )

		# Create prev and next buttons
		self.prev_button = wx.BitmapButton( self, -1, images.getLeftArrowBitmap(), style=wx.NO_BORDER )
		self.prev_button.Bind( wx.EVT_BUTTON, self.GetPrev )
		self.prev_button.Enable( False )
		self.botbar.Add( self.prev_button, 0, wx.EXPAND )
		self.pager = wx.StaticText( self, -1, ' ' )
		self.botbar.Add( self.pager, 1, wx.ALIGN_CENTER )
		self.next_button = wx.BitmapButton( self, -1, images.getRightArrowBitmap(), style=wx.NO_BORDER )
		self.next_button.Bind( wx.EVT_BUTTON, self.GetNext )
		self.botbar.Add( self.next_button, 0, wx.EXPAND )
		self.reset_values()
		# The whole mess
		self.sizer = wx.BoxSizer( wx.VERTICAL )
		self.sizer.Add( self.topbar, 0, wx.EXPAND )
		self.sizer.Add( self.list, 1, wx.EXPAND )
		self.sizer.Add( self.botbar, 0, wx.EXPAND )
		# Set the drag and drop events
		#if wx.Platform != '__WXMAC__':
		self.list.Bind( wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag )
		self.SetSizer( self.sizer )
		self.SetAutoLayout( 1 )
		if self.account.window: self.account.window.SetStatusText( "Ready" )
		wx.Yield()
		self.Layout()
		self.Show()

	def OnZoom( self, event ):
		if self.account.window: self.account.window.OnZoom( 4 )

	def OnRelChange( self, event ):
		self.filter_dirty = True
		if self.current_rel == '=':
			self.current_rel = '~'
			self.find_rel_button.SetBitmapLabel( images.getContainsBitmap() )
		elif self.current_rel == '~':
			self.current_rel = '!='
			self.find_rel_button.SetBitmapLabel( images.getNotEqualsBitmap() )
		elif self.current_rel == '!=':
			self.current_rel = '!~'
			self.find_rel_button.SetBitmapLabel( images.getNotContainsBitmap() )
		elif self.current_rel == '!~':
			self.current_rel = '='
			self.find_rel_button.SetBitmapLabel( images.getEqualsBitmap() )
		self.find_text.SetFocus()
		self.update_next_prev_buttons()

	def create_menu_items( self ):
		if self.trash:
			menu = wx.Menu()
			menu.Append( ID_CONTACT_RECOVER, "&Recover", " Recover the selected contacts" )
			menu.Append( ID_CONTACT_EMPTY, "&Empty Trash", " Permanently deletes all trashed contacts" )
			self.contact_menu = menu
			wx.EVT_MENU( self, ID_CONTACT_RECOVER, self.recover_selected )
			wx.EVT_MENU( self, ID_CONTACT_EMPTY, self.empty_trash )
		elif self.main:
			menu = wx.Menu()
			menu.Append( ID_CONTACT_CREATE, "&Create New", " Create a new contact" )
			menu.Append( ID_CONTACT_EDIT, "&Edit", " Edit the selected contact" )
			menu.Append( ID_CONTACT_DESIGNATE, "De&signate for Preview", " Designate this contact to be used for previewing." )
			menu.Append( ID_CONTACT_CLEAR_DESIGNATE, "C&lear Designation", " Clear designation of this contact for previewing." )
			menu.AppendSeparator()
			menu.Append( ID_CONTACT_COPY, "Copy", " Copy the selected contacts to the clipboard." )
			menu.Append( ID_CONTACT_COPY_UUID, "Copy &UUID", " Copy this contact's UUID to the clipboard." )
			menu.AppendSeparator()
			menu.Append( ID_CONTACT_DELETE, "&Delete", " Delete the selected contacts" )
			self.contact_menu = menu
			wx.EVT_MENU( self, ID_CONTACT_CREATE, self.create_new )
			wx.EVT_MENU( self, ID_CONTACT_EDIT, self.OnContactEdit )
			wx.EVT_MENU( self, ID_CONTACT_DESIGNATE, self.OnContactDesignate )
			wx.EVT_MENU( self, ID_CONTACT_CLEAR_DESIGNATE, self.ClearDesignation )
			wx.EVT_MENU( self, ID_CONTACT_COPY, self.OnContactCopy )
			wx.EVT_MENU( self, ID_CONTACT_COPY_UUID, self.OnContactCopyUUID )
			wx.EVT_MENU( self, ID_CONTACT_DELETE, self.delete_selected )
		else:
			menu = wx.Menu()
			menu.Append( ID_CONTACT_EDIT, "&Edit", " Edit the selected contact" )
			menu.Append( ID_CONTACT_DESIGNATE, "De&signate for Preview", " Designate this contact to be used for previewing." )
			menu.Append( ID_CONTACT_CLEAR_DESIGNATE, "C&lear Designation", " Clear designation of this contact for previewing." )
			menu.AppendSeparator()
			menu.Append( ID_CONTACT_COPY_UUID, "Copy &UUID", " Copy this contact's UUID to the clipboard." )
			menu.AppendSeparator()
			#menu.Append( ID_CONTACT_COPY, "Copy", " Copy the selected contacts to the clipboard." )
			menu.Append( ID_CONTACT_PASTE, "&Add Contacts from Clipboard", " Add contacts from the clipboard." )
			menu.Append( ID_CONTACT_REMOVE, "&Remove Contacts from this Group", " Remove membership from the selected contact" )
			menu.Append( ID_CONTACT_DELETE, "&Delete", " Delete the selected contacts" )
			self.contact_menu = menu
			wx.EVT_MENU( self, ID_CONTACT_EDIT, self.OnContactEdit )
			wx.EVT_MENU( self, ID_CONTACT_DESIGNATE, self.OnContactDesignate )
			wx.EVT_MENU( self, ID_CONTACT_CLEAR_DESIGNATE, self.ClearDesignation )
			#wx.EVT_MENU( self, ID_CONTACT_COPY, self.OnContactCopy )
			wx.EVT_MENU( self, ID_CONTACT_PASTE, self.OnContactPaste )
			wx.EVT_MENU( self, ID_CONTACT_COPY_UUID, self.OnContactCopyUUID )
			wx.EVT_MENU( self, ID_CONTACT_REMOVE, self.remove_selected )
			wx.EVT_MENU( self, ID_CONTACT_DELETE, self.delete_selected )

	def OnContactEdit( self, event ):
		#~ print "OnContactEdit"
		for c in self.get_selected():
			self.currentItem= c.list_index
			self.list.Select( c.list_index )
			self.OnEditContact( None )

	def OnContactDesignate( self, event ):
		self.ClearDesignation()
		try: c = self.get_selected()[0]
		except: return
		if c is None: return
		self.account.window.designated_contact_uuid = c["uuid"]
		self.account.designated = c["uuid"]
		item = self.list.GetItem( c.list_index )
		item.SetTextColour( wx.RED )
		self.list.SetItem( item )

	def OnContactCopy( self, event ):
		cons = self.get_selected()
		if len( cons ) < 1: return
		data = wx.PyTextDataObject()
		xmlstring = '<contacts>'
		for con in cons:
			xmlstring = xmlstring + con.toxml()
		xmlstring = xmlstring + '</contacts>'
		clipdata = wx.TextDataObject()
		clipdata.SetText( xmlstring )
		wx.TheClipboard.Open()
		wx.TheClipboard.SetData( clipdata )
		wx.TheClipboard.Close()

	def OnContactPaste( self, event ):
		if self.main: return
		wx.TheClipboard.Open()
		clipdata = wx.TextDataObject()
		wx.TheClipboard.GetData( clipdata )
		wx.TheClipboard.Close()
		xmlstring = clipdata.GetText()
		xmlstring = xmlstring.strip()
		#parse the data
		sdom  = None
		snode = None
		try:
			sdom 	= minidom.parseString( xmlstring )
			snode	= sdom.firstChild
		except:
			common.show_error( "Failed to parse pasted data", False )
			del sdom
			del snode
			return
		if snode.nodeName != 'contacts': return
		# We are adding a contact or two
		self.account.window.SetStatusText( "Applying for membership ..." )
		wx.Yield()
		cdom = '<instructions>'
		cons = []
		for inode in snode.getElementsByTagName("contact"):
			con = rex.contacts.contact( self.account, inode.toxml() )
			cons.append( con )
			cdom = cdom + "<instruction action=\"%s\" group_id=\"%s\" contact_id=%s />" % \
				( "add", self.rex_object["id"], saxutils.quoteattr( con["id"] ) )
		cdom = cdom + "</instructions>"
		try:
			result = self.account.execute( 'base', "memberships_addremove", DOM=cdom )
		except:
			common.show_error('Failed to modify membership')
			return None
		if len( cons ):
			cache = self.get_cache()
			self.filter_dirty = True
		for con in cons:
			if len( con['memberships'] ) > 0: con['memberships'] = con['memberships'] + ","
			con['memberships'] = con['memberships'] + self.rex_object["id"]
			self.AppendContact( con )
			cache.append( con )
		self.resize_columns()
		self.account.window.SetStatusText( "Ready" )

	def OnContactCopyUUID( self, event ):
		try: c = self.get_selected()[0]
		except: return
		if c is None: return
		uuid = c["uuid"]
		# Now let's put it in the clipboard
		clipdata = wx.TextDataObject()
		clipdata.SetText( uuid )
		wx.TheClipboard.Open()
		wx.TheClipboard.SetData( clipdata )
		wx.TheClipboard.Close()

	def OnFindTextKeyPress( self, event ):
		self.filter_dirty = True
		self.current_pattern = self.find_text.GetValue()
		self.update_next_prev_buttons()
		common.dprint("current pattern now", self.current_pattern )

	def ClearDesignation( self, event = None ):
		self.account.window.designated_contact_uuid = ''
		count = self.list.GetItemCount()
		if count < 1: return
		for i in range( 0, count ):
			item = self.list.GetItem( i )
			item.SetTextColour( self.item_color )
			self.list.SetItem( item )

	def OnTimer( self, event ):
		self.GetPage()

	def OnBeginDrag( self, event ):
		cons = self.get_selected()
		if len( cons ) < 1: return
		data = wx.PyTextDataObject()
		xmlstring = '<contacts>'
		for con in cons:
			xmlstring = xmlstring + con.toxml()
		xmlstring = xmlstring + '</contacts>'
		data.SetText( xmlstring )
		ds = wx.DropSource( self )
		ds.SetData( data )
		result = ds.DoDragDrop()
		common.dprint( "begindrag:", result )
		if (not self.main):
			# dropped out in the wild
			if result != 5: return
			self.remove_selected()

	def remove_selected( self, event = None ):
		cons = self.get_selected()
		if len( cons ) < 1: return
		# Construct the DOM
		cdom = "<instructions>"
		for con in cons:
			cdom = cdom + "<instruction action=\"%s\" group_id=\"%s\" contact_id=%s />" % \
					( "remove", self.rex_object["id"], saxutils.quoteattr( con["id"] ) )
		cdom = cdom + "</instructions>"
		try:
			result = self.account.execute( 'base', "memberships_addremove", DOM=cdom )
		except:
			common.show_error("Failed to modify the membership")
			return
		if result:
			# Refresh this page
			self.GetPage( True )

	def reset_values( self ):
		self.contacts 			= {}
		self.variable_choice.SetSelection( 0 )
		self.current_variable 	= ''
		self.current_rel		= '='
		self.current_pattern	= ''
		self.current_max		= 30
		self.current_page		= 0
		self.total_pages		= 0
		self.total_contacts		= 0
		self.filter_dirty 		= False

	def resize_columns( self ):
		'''Used internally to recalculate the column sizes'''
		# Set the column header sizes
		for i in range( self.list.GetColumnCount() ):
			self.list.SetColumnWidth( i, wx.LIST_AUTOSIZE )

	def OnShowByChange( self, event ):
		variable = self.variable_choice.GetStringSelection()
		variable = variable.strip()
		if variable == self.current_variable: return
		self.filter_dirty = True
		self.current_variable = variable
		self.find_text.Enable( self.current_variable != '' )
		self.find_rel_button.Enable( self.current_variable != '' )
		if self.current_variable != '':
			self.find_text.SetFocus()
		self.update_next_prev_buttons()

	def OnRPPChange( self, event ):
		sel = self.rpp_choice.GetSelection()
		if sel == 0 :
			self.current_max = 10
		elif sel == 1 :
			self.current_max = 30
		elif sel == 2 :
			self.current_max = 50
		elif sel == 3 :
			self.current_max = 100
		else:
			self.current_max = 10

	def AppendContact( self, con ):
		x = self.list.GetItemCount()
		cname = con['first_name'] + " " + con['last_name']
		x = self.list.InsertImageStringItem( sys.maxint, cname, self.contact_icon )
		self.list.SetStringItem( x , 1, con["email"] )
		self.list.SetStringItem( x , 2, con["id"] )
		self.list.SetStringItem( x , 3, con["xid"] )
		try: self.list.SetStringItem( x , 4, con["username"] )
		except: pass
		try: self.list.SetStringItem( x , 5, con["password"] )
		except: pass
		self.list.SetStringItem( x , 6, con['memberships'] )
		self.list.SetStringItem( x , 7, con['subscriptions'] )
		self.list.SetStringItem( x , 8, con['blocks'] )
		self.list.SetStringItem( x , 9, con['instances'] )
		self.list.SetStringItem( x , 10,con["uuid"] )
		self.list.SetStringItem( x , 11,con["created_on"] )
		if self.trash:
			self.list.SetStringItem( x , 12,con["trashed_on"] )
		self.list.SetItemData( x, x )
		con.list_item = self.list.GetItem( x )
		# Is this the designated contact ?
		try:
			if con["uuid"] == self.account.window.designated_contact_uuid:
				con.list_item.SetTextColour( wx.RED )
				self.list.SetItem( con.list_item )
		except:
			pass
		return x

	def OnRefresh( self, event=None ):
		self.GetPage( True )

	def GetPage( self, refresh=False ):
		# Set the filter attibs,
		self.list.DeleteAllItems()
		if self.filter_dirty:
			self.contacts		= {}
			self.filter_dirty	= False
			self.current_page	= 1
		elif refresh:
			try: del self.contacts[ self.current_page ]
			except: pass
			if self.current_page == 0: self.current_page = 1

		# Update the status bar
		if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
		wx.Yield()
		wx.Yield()
		wx.Yield()
		# Do we use the cache or download ?
		try:
			cache = self.contacts[ self.current_page ]
		except KeyError:
			cache = None

		if cache:
			for con in cache:
				con.list_index = self.AppendContact( con )
		else:
			# Make the call
			trash = '0'
			if self.trash: trash = '1'
			group_id = ''
			if not self.main:
				group_id = " group_id=\"%s\" " % self.rex_object['id']
			cdom = "<contacts_get variable=\"%s\" relation=\"%s\" pattern=\"%s\" max=\"%s\" page=\"%s\" trash=\"%s\" %s />" % \
					( self.current_variable, self.current_rel, self.current_pattern, self.current_max, self.current_page, trash, group_id )
			cur = wx.BusyCursor()
			cnts = None
			try:
				xmlstring = self.account.execute( 'base', "contacts_get2", DOM=cdom )
				#xmlstring = common.uni2str( xmlstring )
				xmlstring = common.prepare_xml( xmlstring )
				cdom 	= minidom.parseString( xmlstring )
				cnts	= cdom.firstChild
			except:
				if self.account.window: self.account.window.SetStatusText( "Ready" )
			del cur
			if not cnts: return
			self.total_pages 	= int( cnts.attributes["pages"].value )
			self.total_contacts = int( cnts.attributes["count"].value )
			self.contacts[ self.current_page ] = []
			cache = self.contacts[ self.current_page ]
			for cnt in cnts.getElementsByTagName("contact"):
				con = None
				try:
					con = rex.contacts.contact( self.account, cnt.toxml() )
				except:
					common.show_error( "failed to parse contact [[%s]]" % cnt.toxml().encode( 'utf-8' ), False )
				if not con: continue
				cache.append( con )
				# Create a list item
				con.list_index = self.AppendContact( con )
			#except Exception:
			#	print "Failed to load account contacts."
		self.resize_columns()
		label = "   Page %s of %s [%s total]   " % (self.current_page, self.total_pages, self.total_contacts)
		#print "Label ", label
		self.pager.SetLabel( label )
		self.update_next_prev_buttons()
		if self.account.window: self.account.window.SetStatusText( "Ready" )
		wx.Yield()


	def GetNext( self, event ):
		# if self.current_page >= self.total_pages: return
		self.timer.Stop()
		self.current_page = self.current_page + 1
		# override?
		if self.filter_dirty:
			self.contacts		= {}
			self.filter_dirty	= False
			self.current_page	= 1

		if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
		#wx.Yield()
		# Cache exists already?
		try:
			cache = self.contacts[ self.current_page ]
			self.GetPage()
			return
		except KeyError:
			pass
		self.timer.Start( 700, 1 )

	def GetPrev( self, event ):
		if self.current_page <= 1: return
		self.timer.Stop()
		self.current_page = self.current_page - 1
		if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
		#wx.Yield()
		# Cache exists already?
		try:
			cache = self.contacts[ self.current_page ]
			self.GetPage()
			return
		except KeyError:
			pass
		self.timer.Start( 700, 1 )

	def update_next_prev_buttons( self ):
		#print "Checking np %s, %s" % ( self.current_page, self.total_pages )
		if self.filter_dirty:
			self.next_button.Enable( True )
			self.prev_button.Enable( False )
			return
		#
		if self.current_page >= self.total_pages:
			#print "DISABLING next_button"
			self.next_button.Enable( False )
		else:
			#print "ENABLING next_button"
			self.next_button.Enable( True )

		if self.current_page <= 1:
			#print "DISABLING prev_button"
			self.prev_button.Enable( False )
		else:
			#print "ENABLING prev_button"
			self.prev_button.Enable( True )

	def OnListRightClick( self, event ):
		try:
			point = event.GetPositionTuple()
			if wx.Platform == '__WXGTK__':
				point = wx.Point( event.m_x + 5, event.m_y + 45 )
			self.PopupMenu( self.contact_menu, point )
		except:
			pass

	def OnItemDoubleClick( self, event ):
		if self.trash: return
		self.OnEditContact( event )

	def OnItemActivated( self, event ):
		self.currentItem = event.m_itemIndex

	def OnEditContact( self, event ):
		if self.trash: return
		con = self.contacts[self.current_page][self.currentItem]
		if con is None: return
		#~ print "Editing contact %s" % con["first_name"]
		if con.editor is not None:
			self.account.window.ebook.SetSelection( common.get_page_index( self.account.window.ebook, con.editor ) )
			return
		con.editor = contacts.contact( self.account, con, self.currentItem )
		# add the page
		self.account.window.ebook.AddPage( con.editor, con.editor.get_label_text() )
		pi = common.get_page_index( self.account.window.ebook, con.editor )
		try:
			self.account.window.ebook.SetSelection( pi )
			self.account.window.ebook.SetPageImage( pi, self.account.window.icon_from_rex_object( con ) )
		except:
			common.show_error("Failed to switch to editor", False)

	def create_new( self, event = None ):
		if self.trash: return
		con = rex.contacts.contact( self.account, None )
		if not con.create(): return
		# Now load the new contact
		cdom = "<contacts_get variable=\"id\" relation=\"=\" pattern=\"%s\" max=\"1\" page=\"1\" />" % \
					( con['id'], )
		xmlstring = self.account.execute( 'base', "contacts_get2", DOM=cdom )
		#try:
		xmlstring = common.uni2str( xmlstring )
		cdom 	= minidom.parseString( xmlstring )
		cnts	= cdom.firstChild
		cache 	= self.get_cache() #self.contacts[ self.current_page ]
		for cnt in cnts.getElementsByTagName("contact"):
			con = rex.contacts.contact( self.account, cnt.toxml() )
			con['first_name'] = 'New'
			con['last_name'] = 'Contact'
			cache.append( con )
			# Create a list item
			con.list_index = self.AppendContact( con )
		#except Exception:
		self.resize_columns()
		self.currentItem = con.list_index
		self.OnEditContact( None )
		self.filter_dirty = True

	def get_cache( self ):
		try:
			cache = self.contacts[ self.current_page ]
		except KeyError:
			self.contacts[ self.current_page ] = []
			cache = self.contacts[ self.current_page ]
		return cache

	def get_selected( self ):
		sel = []
		count = self.list.GetItemCount()
		if count < 1: return sel
		for i in range( 0, count ):
			if self.list.IsSelected( i ):
				sel.append( self.contacts[ self.current_page ][i] )
		return sel

	def recover_selected( self, event = None ):
		common.dprint("Recover selected")
		# Confirm
		cons = self.get_selected()
		if len( cons ) < 1: return
		# Construct the DOM
		cdom = ""
		for con in cons:
			cdom = cdom + ( "<c id=\"%s\" />" % con["id"] )
		cdom = "<cs>" + cdom + "</cs>"
		try:
			res = self.account.execute( 'base', "contacts_recover", DOM=cdom )
		except:
			common.show_error("Failed to recover")
			return
		if res:
			# Refresh this page
			self.GetPage( True )
			
	def empty_trash( self, event = None ):
		common.dprint("Empty Trash")
		# Confirm
		dlg = wx.Dialog( self, -1, "Empty trash", pos=wx.DefaultPosition, style=wx.DEFAULT_DIALOG_STYLE, size=(350, 300) )
		# build the widget panels
		selection_panel = wx.Panel( dlg, -1 ) # to hold the radio buttons
		button_panel = wx.Panel( dlg, -1 )    # to hold the ok and cancel buttons
		
		sel_sizer = wx.BoxSizer( wx.VERTICAL )
		all = wx.RadioButton( selection_panel, ID_TRASH_ALL, "Permanently delete all trashed contacts" )
		selected = wx.RadioButton( selection_panel, ID_TRASH_SELECTED, "Permanently delete contacts trashed on or before:" )
		date = wx.calendar.CalendarCtrl( selection_panel, ID_TRASH_DATE, wx.DateTime_Now(), pos = wx.DefaultPosition, style = wx.calendar.CAL_SHOW_HOLIDAYS|wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION )
		sel_sizer.Add( all, 1 )
		sel_sizer.Add( selected, 1 )
		sel_sizer.Add( date, 4, wx.ALIGN_CENTER|wx.TOP )
		selection_panel.SetSizer( sel_sizer )
		bp_sizer = wx.BoxSizer( wx.HORIZONTAL )
		bp_sizer.Add( wx.Button( button_panel, wx.ID_OK, " OK" ), 10, wx.ALIGN_CENTER, 5 )
		bp_sizer.Add( wx.StaticText(self, -1, '' ), 1 )
		bp_sizer.Add( wx.Button( button_panel, wx.ID_CANCEL, " Cancel" ), 10, wx.ALIGN_CENTER, 5 )
		button_panel.SetSizer( bp_sizer )
		bp_sizer.Layout()
		# position everything
		vbox = wx.BoxSizer( wx.VERTICAL )
		vbox.Add( selection_panel, 3, wx.EXPAND | wx.ALL, 5 )
		vbox.Add( button_panel, 1, wx.ALIGN_CENTER, 5 )
		dlg.SetSizer( vbox )
		#bind the widgets
		# show the dialog
		res = dlg.ShowModal()
		# bail if we cancelled
		if( res != wx.ID_OK ): return False
		# Construct the DOM
		tdom = ""
		if( selected.GetValue() ):
			tdom = "<trash date=\"" + date.GetDate().Format( "%Y-%m-%d" ) + "\"/>"
			common.dprint( "Date: " + date.GetDate().Format( "%Y-%m-%d" ) )
		else:
			tdom = "<trash/>"
		# Make the call
		try:
			res = self.account.execute( 'base', "empty_trash", DOM=tdom )
		except:
			common.show_error("Failed to empty")
			return
		if res:
			# Refresh this page
			self.GetPage( True )

	def delete_selected( self, event = None ):
		# Confirm
		res = wx.MessageBox( "Are you sure you want to delete selected contact(s)?", "Confirm Delete", wx.YES_NO )
		if res != wx.YES: return
		cons = self.get_selected()
		if len( cons ) < 1: return
		# Construct the DOM
		cdom = ""
		for con in cons:
			cdom = cdom + ( "<c id=\"%s\" />" % con["id"] )
		cdom = "<cs>" + cdom + "</cs>"
		try:
			res = self.account.execute( 'base', "contacts_delete", DOM=cdom )
		except:
			common.show_error("Failed to delete")
			return
		if res:
			# Refresh this page
			self.GetPage( True )

	def for_group( self, grp ):
		self.qualifier	= "GROUP"
		self.qid		= grp["id"]
		self.rex_object = grp

	#~ def for_pub_sub( self, pub ):
		#~ self.qualifier	= "PUBSUB"
		#~ self.qid		= pub["id"]

	#~ def for_pub_BLK( self, BLK ):
		#~ self.qualifier	= "PUBBLK"
		#~ self.qid		= pub["id"]

	def set_value( self, value ):
		pass
		#~ common.dprint( self.__class__, 'set value', value )

	def get_value( self ):
		pass
		#~ common.dprint( self.__class__, 'get value' )

	def set_readwrite( self, rw ):
		pass
		#~ common.dprint( self.__class__, 'set readwrite', rw )

####################################################################################################
class group_control( wx.Panel ):
	def __init__( self, parent, account, main ):
		wx.Panel.__init__( self, parent )
		self.parent = parent
		#self.sui    = sui
		self.account = account
		self.qualifier	= ""
		self.qid		= ""
		self.main		= main
		self.create_menu_items()
		# Connect a timer to this frame
		self.timer = wx.Timer( self, ID_CGTIMER_TIMER )
		wx.EVT_TIMER( self, ID_CGTIMER_TIMER, self.OnTimer )
		# Create the top bar
		self.topbar = wx.BoxSizer( wx.HORIZONTAL )
		# The new button
		if self.main:
			self.new_button = wx.BitmapButton( self, -1, images.getNewContactBitmap(), size=common.button_size, style=wx.NO_BORDER )
			self.new_button.Bind( wx.EVT_BUTTON, self.create_new )
			self.topbar.Add( self.new_button, 0 )
		# The refresh button
		self.refresh_button = wx.BitmapButton( self, -1, images.getRefreshBitmap(), size=common.button_size, style=wx.NO_BORDER )
		self.refresh_button.Bind( wx.EVT_BUTTON, self.OnRefresh )
		self.topbar.Add( self.refresh_button, 0 )
		# The show selector
		variables = ['', 'id','name', 'description', 'super block', 'super group', 'subscribed to pid', 'blocked from pid' ]
		self.variable_choice = wx.Choice( self, -1, choices=variables )
		self.variable_choice.SetSelection( 0 )
		self.variable_choice.Bind( wx.EVT_CHOICE, self.OnShowByChange )
		self.topbar.Add( self.variable_choice, 1, wx.EXPAND )
		# the relation button
		self.find_rel_button = wx.BitmapButton( self, -1, images.getEqualsBitmap(), size=common.button_size, style=wx.NO_BORDER )
		self.find_rel_button.Bind( wx.EVT_BUTTON, self.OnRelChange )
		self.find_rel_button.Enable( False )
		self.topbar.Add( self.find_rel_button, 0 )
		# The find text box
		self.find_text = wx.TextCtrl( self, -1, '', style=wx.TE_PROCESS_ENTER )
		self.find_text.Enable( False )
		self.find_text.Bind( wx.EVT_TEXT, self.OnFindTextKeyPress )
		self.find_text.Bind( wx.EVT_TEXT_ENTER, self.OnRefresh )
		self.topbar.Add( self.find_text, 2, wx.EXPAND )
		# The finding selector
		self.rpp_choice = wx.Choice( self, ID_RPP, choices=["10 per page", "30 per page","50 per page", "100 per page"] )
		self.rpp_choice.SetSelection( 1 )
		wx.EVT_CHOICE( self, ID_RPP, self.OnRPPChange )
		self.topbar.Add( self.rpp_choice, 1, wx.EXPAND )
		#self.find_by = 1	# Find by ID by default
		# zoom
		if main:
			self.zoom_button = wx.BitmapButton( self, -1, images.getZoomBitmap(), size=common.button_size, style=wx.NO_BORDER )
			self.zoom_button.Bind( wx.EVT_BUTTON, self.OnZoom )
			self.topbar.Add( self.zoom_button, 0 )
		# Enable the top bar controls
		self.reset_values()
		#~ self.enable_topbar( True )
		self.filter_set = False
		# Create the list
		self.list = wx.ListCtrl( self, ID_GROUPS_LIST, style=wx.LC_REPORT )
		self.il = wx.ImageList(16, 16)
		# Load our images
		self.group_icon 	= self.il.Add( images.getGroupBitmap() )
		self.list.SetImageList( self.il, wx.IMAGE_LIST_SMALL )
		self.list.Bind( wx.EVT_LEFT_DCLICK, self.OnItemDoubleClick )
		# default?
		self.list.Bind( wx.EVT_RIGHT_DOWN, self.OnListRightClick )
		# for wxGTK
		#self.list.Bind( wx.EVT_RIGHT_UP, self.OnListRightClick )
		# for wxMSW
		#self.list.Bind( wx.EVT_COMMAND_RIGHT_CLICK, self.OnListRightClick )
		self.Bind( wx.EVT_LIST_ITEM_SELECTED, self.OnItemActivated, id=ID_GROUPS_LIST )
		self.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, id=ID_GROUPS_LIST )
		# Set the drop target
		dt = drop_target( self, self.main )
		self.list.SetDropTarget( dt )
		# Add it's columns
		info = wx.ListItem()
		info.m_mask = wx.LIST_MASK_TEXT # | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
		#info.m_image = -1
		info.m_format = 0
		info.m_text = "Name"
		self.list.InsertColumnInfo(0, info)

		info.m_text = "ID"
		self.list.InsertColumnInfo(1, info)

		info.m_text = "Members"
		self.list.InsertColumnInfo(2, info)
		
		info.m_text = "Licenses"
		self.list.InsertColumnInfo(3, info)

		info.m_text = "Subscribed"
		self.list.InsertColumnInfo(4, info)

		info.m_text = "Blocked"
		self.list.InsertColumnInfo(5, info)

		info.m_text = "Instances"
		self.list.InsertColumnInfo(6, info)

		info.m_text = "Super Block"
		self.list.InsertColumnInfo(7, info)

		info.m_text = "Super Group"
		self.list.InsertColumnInfo(8, info)

		# Resize them all
		self.resize_columns()

		# Create the bottom bar
		self.botbar = wx.BoxSizer( wx.HORIZONTAL )

		# Create prev and next buttons
		self.prev_button = wx.BitmapButton( self, -1, images.getLeftArrowBitmap(), style=wx.NO_BORDER )
		self.prev_button.Bind( wx.EVT_BUTTON, self.GetPrev )
		self.prev_button.Enable( False )
		self.botbar.Add( self.prev_button, 0, wx.EXPAND )
		self.pager = wx.StaticText( self, -1, " " )
		self.botbar.Add( self.pager, 1, wx.ALIGN_CENTER )
		self.next_button = wx.BitmapButton( self, -1, images.getRightArrowBitmap(), style=wx.NO_BORDER )
		self.next_button.Bind( wx.EVT_BUTTON, self.GetNext )
		self.botbar.Add( self.next_button, 0, wx.EXPAND )
		# The whole mess
		self.sizer = wx.BoxSizer( wx.VERTICAL )
		self.sizer.Add( self.topbar, 0, wx.EXPAND )
		self.sizer.Add( self.list, 1, wx.EXPAND )
		self.sizer.Add( self.botbar, 0, wx.EXPAND )
		# Set the drag and drop events
		#if wx.Platform != '__WXMAC__':
		self.list.Bind( wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag )
		self.SetSizer( self.sizer )
		self.SetAutoLayout( 1 )
		if self.account.window: self.account.window.SetStatusText( "Ready" )
		wx.Yield()
		self.Layout()
		self.Show()

	def OnRelChange( self, event ):
		self.filter_dirty = True
		if self.current_rel == '=':
			self.current_rel = '~'
			self.find_rel_button.SetBitmapLabel( images.getContainsBitmap() )
		elif self.current_rel == '~':
			self.current_rel = '!='
			self.find_rel_button.SetBitmapLabel( images.getNotEqualsBitmap() )
		elif self.current_rel == '!=':
			self.current_rel = '!~'
			self.find_rel_button.SetBitmapLabel( images.getNotContainsBitmap() )
		elif self.current_rel == '!~':
			self.current_rel = '='
			self.find_rel_button.SetBitmapLabel( images.getEqualsBitmap() )
		self.find_text.SetFocus()
		self.update_next_prev_buttons()

	def create_menu_items( self ):
		if self.main:
			menu = wx.Menu()
			menu.Append( ID_GROUP_CREATE, "&Create New", " Create a new group" )
			menu.Append( ID_GROUP_EDIT, "&Edit", " Edit the selected groups" )
			menu.AppendSeparator()
			menu.Append( ID_GROUP_COPY, "&Copy", " Copy the selected groups to the clipboard" )
			menu.AppendSeparator()
			menu.Append( ID_GROUP_DELETE, "&Delete", " Delete the selected groups" )
			menu.Append( ID_GROUP_EXPUNGE, "&Delete with Members", " Delete the selected groups and all members" )
			self.group_menu = menu
			self.Bind( wx.EVT_MENU, self.create_new, id=ID_GROUP_CREATE )
			self.Bind( wx.EVT_MENU, self.OnGroupEdit, id=ID_GROUP_EDIT )
			self.Bind( wx.EVT_MENU, self.OnGroupCopy, id=ID_GROUP_COPY )
			self.Bind( wx.EVT_MENU, self.delete_selected, id=ID_GROUP_DELETE )
			self.Bind( wx.EVT_MENU, self.expunge_selected, id=ID_GROUP_EXPUNGE )
		else:
			menu = wx.Menu()
			menu.Append( ID_GROUP_EDIT, "&Edit", " Edit the selected group" )
			menu.AppendSeparator()
			#menu.Append( ID_GROUP_COPY, "&Copy", " Copy the selected groups to the clipboard" )
			menu.Append( ID_GROUP_PASTE, "&Paste", " Pase contacts from the clipboard." )
			menu.Append( ID_GROUP_REMOVE, "&Cut", " Remove membership to the selected groups" )
			menu.AppendSeparator()
			menu.Append( ID_GROUP_DELETE, "&Delete", " Delete the selected groups" )
			menu.Append( ID_GROUP_EXPUNGE, "&Delete with Members", " Delete the selected groups and all members" )
			self.group_menu = menu
			self.Bind( wx.EVT_MENU, self.create_new, id=ID_GROUP_CREATE )
			self.Bind( wx.EVT_MENU, self.OnGroupEdit, id=ID_GROUP_EDIT )
			#self.Bind( wx.EVT_MENU, self.OnGroupCopy, id=ID_GROUP_COPY )
			self.Bind( wx.EVT_MENU, self.OnGroupPaste, id=ID_GROUP_PASTE )
			self.Bind( wx.EVT_MENU, self.delete_selected, id=ID_GROUP_DELETE )
			self.Bind( wx.EVT_MENU, self.remove_selected, id=ID_GROUP_REMOVE )
			self.Bind( wx.EVT_MENU, self.expunge_selected, id=ID_GROUP_EXPUNGE )

	def OnZoom( self, event ):
		if self.account.window: self.account.window.OnZoom( 4 )

	def OnGroupCopy( self, event ):
		grps = self.get_selected()
		if len( grps ) < 1: return
		data = wx.PyTextDataObject()
		xmlstring = '<groups>'
		for grp in grps:
			xmlstring = xmlstring + grp.toxml()
		xmlstring = xmlstring + '</groups>'
		clipdata = wx.TextDataObject()
		clipdata.SetText( xmlstring )
		wx.TheClipboard.Open()
		wx.TheClipboard.SetData( clipdata )
		wx.TheClipboard.Close()

	def OnGroupPaste( self, event ):
		if self.main: return
		wx.TheClipboard.Open()
		clipdata = wx.TextDataObject()
		wx.TheClipboard.GetData( clipdata )
		wx.TheClipboard.Close()
		xmlstring = clipdata.GetText()
		xmlstring = xmlstring.strip()
		common.dprint("Got data", xmlstring)
		#parse the data
		sdom  = None
		snode = None
		try:
			sdom 	= minidom.parseString( xmlstring )
			snode	= sdom.firstChild
		except:
			common.show_error( "Failed to parse pasted data", False )
			del sdom
			del snode
			return
		if snode.nodeName != 'groups': return
		# We are adding a group or two or more :)
		self.account.window.SetStatusText( "Applying for membership ..." )
		wx.Yield()
		cdom = '<instructions>'
		grps = []
		for inode in snode.getElementsByTagName("group"):
			grp = rex.contacts.group( self.account, inode.toxml() )
			grps.append( grp )
			cdom = cdom + "<instruction action=\"%s\" group_id=\"%s\" contact_id=%s />" % \
				( "add", grp["id"], saxutils.quoteattr( self.rex_object["id"] ) )
		cdom = cdom + "</instructions>"
		try:
			result = self.account.execute( 'base', "memberships_addremove", DOM=cdom )
		except:
			common.show_error('Failed to modify membership')
			return
		# Add to the list
		if len( grps ):
			cache = self.get_cache()
			self.clear_cache = True
			for grp in grps:
				if grp['memberships'].strip() == '': grp['memberships'] = 0
				grp['memberships'] = str( int( grp['memberships'] ) + 1 )
				self.AppendGroup( grp )
				cache.append( grp )
			self.resize_columns()
		self.account.window.SetStatusText( "Ready" )


	def OnGroupEdit( self, event ):
		for g in self.get_selected():
			self.currentItem= g.list_index
			self.list.Select( g.list_index )
			self.OnEditGroup( None )

	def OnFindTextKeyPress( self, event ):
		self.filter_dirty = True
		self.current_pattern = self.find_text.GetValue()
		self.update_next_prev_buttons()

	def OnTimer( self, event ):
		self.GetPage()

	def OnBeginDrag( self, event ):
		grps = self.get_selected()
		if len( grps ) < 1: return
		data = wx.PyTextDataObject()
		xmlstring = '<groups>'
		for grp in grps:
			xmlstring = xmlstring + grp.toxml()
		xmlstring = xmlstring + '</groups>'
		data.SetText( xmlstring )
		ds = wx.DropSource( self )
		ds.SetData( data )
		result = ds.DoDragDrop()
		common.dprint( "begindrag:", result )
		if (not self.main):
			# dropped out in the wild
			if result != 5: return
			self.remove_selected()

	def remove_selected( self, event = None ):
		grps = self.get_selected()
		if len( grps ) < 1: return
		# Construct the DOM
		cdom = "<instructions>"
		for grp in grps:
			cdom = cdom + "<instruction action=\"%s\" group_id=%s contact_id=\"%s\" />" % \
					( "remove", saxutils.quoteattr( grp["id"] ), self.rex_object["id"] )
		cdom = cdom + "</instructions>"
		try:
			result = self.account.execute( 'base', "memberships_addremove", DOM=cdom )
		except:
			common.show_error("Failed to modify the membership")
			return
		if result:
			# Refresh this page
			self.GetPage( True )

	def reset_values( self ):
		self.groups 			= {}
		self.variable_choice.SetSelection( 0 )
		self.current_variable 	= ''
		self.current_rel		= '='
		self.current_pattern	= ""
		self.current_max		= 10
		self.current_page		= 0
		self.total_pages		= 0
		self.total_groups		= 0
		self.filter_dirty 		= False

	def resize_columns( self ):
		'''Used internally to recalculate the column sizes'''
		# Set the column header sizes
		for i in range( self.list.GetColumnCount() ):
			self.list.SetColumnWidth( i, wx.LIST_AUTOSIZE )

	def OnShowByChange( self, event ):
		variable = self.variable_choice.GetStringSelection()
		variable = variable.strip()
		if variable == self.current_variable: return
		self.filter_dirty = True
		self.current_variable = variable
		self.find_text.Enable( self.current_variable != '' )
		self.find_rel_button.Enable( self.current_variable != '' )
		if self.current_variable != '':
			self.find_text.SetFocus()
		self.update_next_prev_buttons()

	def OnRPPChange( self, event ):
		sel = self.rpp_choice.GetSelection()
		if sel == 0 :
			self.current_max = 10
		elif sel == 1 :
			self.current_max = 30
		elif sel == 2 :
			self.current_max = 50
		elif sel == 3 :
			self.current_max = 100
		else:
			self.current_max = 10

	def AppendGroup( self, grp ):
		x = self.list.InsertImageStringItem( sys.maxint, grp["name"], self.group_icon )
		self.list.SetStringItem( x , 1, grp["id"] )
		self.list.SetStringItem( x , 2, grp['memberships'] )
		self.list.SetStringItem( x , 3, grp['licenses'] )
		self.list.SetStringItem( x , 4, grp['subscriptions'] )
		self.list.SetStringItem( x , 5, grp['blocks'] )
		self.list.SetStringItem( x , 6, grp['instances'] )
		self.list.SetStringItem( x , 7, grp['super_block'] )
		self.list.SetStringItem( x , 8, grp['super_group'] )
		self.list.SetItemData( x, x )
		grp.list_item = self.list.GetItem( x )
		return x

	def OnRefresh( self, event=None ):
		self.GetPage( True )

	def GetPage( self, refresh=False ):
		# Set the filter attibs,
		self.list.DeleteAllItems()
		if self.filter_dirty:
			self.groups			= {}
			self.filter_dirty	= False
			self.current_page	= 1
		elif refresh:
			try: del self.groups[ self.current_page ]
			except: pass
			if self.current_page == 0: self.current_page = 1

		# Update the status bar
		if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
		wx.Yield()
		wx.Yield()
		wx.Yield()
		# Do we use the cache or download ?
		try:
			cache = self.groups[ self.current_page ]
		except KeyError:
			cache = None

		if cache:
			for grp in cache:
				grp.list_index = self.AppendGroup( grp )
		else:
			# Make the call
			contact_id = ''
			if not self.main:
				contact_id = " contact_id=\"%s\" " % self.rex_object['id']
			cdom = u"<groups_get variable=\"%s\" relation=\"%s\" pattern=\"%s\" max=\"%s\" page=\"%s\" %s />" % \
					( self.current_variable, self.current_rel, self.current_pattern, self.current_max, self.current_page, contact_id )
			cur = wx.BusyCursor()
			gs = None
			try:
				xmlstring = self.account.execute( 'base', "groups_get2", DOM=cdom )
				xmlstring = common.uni2str( xmlstring )
				cdom 	= minidom.parseString( xmlstring )
				gs		= cdom.firstChild
			except:
				if self.account.window: self.account.window.SetStatusText( "Ready" )
			del cur
			if not gs: return
			self.total_pages 	= int( gs.attributes["pages"].value )
			self.total_groups = int( gs.attributes["count"].value )
			self.groups[ self.current_page ] = []
			cache = self.groups[ self.current_page ]
			for g in gs.getElementsByTagName("group"):
				grp = rex.contacts.group( self.account, g.toxml() )
				cache.append( grp )
				# Create a list item
				grp.list_index = self.AppendGroup( grp )
			#except Exception:
			#	print "Failed to load account contacts."
		self.resize_columns()
		label = "   Page %s of %s [%s total]   " % (self.current_page, self.total_pages, self.total_groups)
		#print "Label ", label
		self.pager.SetLabel( label )
		self.update_next_prev_buttons()
		wx.Yield()

	def GetNext( self, event ):
		self.timer.Stop()
		self.current_page = self.current_page + 1
		# override?
		if self.filter_dirty:
			self.contacts		= {}
			self.filter_dirty	= False
			self.current_page	= 1

		if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
		wx.Yield()
		# Cache exists already?
		try:
			cache = self.groups[ self.current_page ]
			self.GetPage()
			return
		except KeyError:
			pass
		self.timer.Start( 700, 1 )

	def GetPrev( self, event ):
		if self.current_page <= 1: return
		self.current_page = self.current_page - 1
		if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
		wx.Yield()
		# Cache exists already?
		try:
			cache = self.groups[ self.current_page ]
			self.GetPage()
			return
		except KeyError:
			pass
		self.timer.Start( 700, 1 )

	def update_next_prev_buttons( self ):
		#print "Checking np %s, %s" % ( self.current_page, self.total_pages )
		if self.filter_dirty:
			self.next_button.Enable( True )
			self.prev_button.Enable( False )
			return

		if self.current_page >= self.total_pages:
			#print "DISABLING next_button"
			self.next_button.Enable( False )
		else:
			#print "ENABLING next_button"
			self.next_button.Enable( True )

		if self.current_page <= 1:
			#print "DISABLING prev_button"
			self.prev_button.Enable( False )
		else:
			#print "ENABLING prev_button"
			self.prev_button.Enable( True )

	def OnListRightClick( self, event ):
		try:
			point = event.GetPositionTuple()
			if wx.Platform == '__WXGTK__':
				point = wx.Point( event.m_x + 5, event.m_y + 45 )
			self.PopupMenu( self.group_menu, point )
		except:
			pass

	def OnItemDoubleClick( self, event ):
		#~ print "OnItemDoubleClick"
		self.OnEditGroup( event )

	def OnItemActivated( self, event ):
		self.currentItem = event.m_itemIndex

	def OnEditGroup( self, event ):
		#~ print "self.current_page ",self.current_page, " self.currentItem", self.currentItem
		grp = self.groups[self.current_page][self.currentItem]
		if self.currentItem < 0: return
		if grp is None: return
		if grp.editor is not None:
			self.account.window.ebook.SetSelection( common.get_page_index( self.account.window.ebook, grp.editor ) )
			return
		grp.editor = contacts.group( self.account, grp, self.currentItem )
		# add the page
		self.account.window.ebook.AddPage( grp.editor, grp.editor.get_label_text() )
		pi = common.get_page_index( self.account.window.ebook, grp.editor )
		try:
			self.account.window.ebook.SetSelection( pi )
			self.account.window.ebook.SetPageImage( pi, self.account.window.icon_from_rex_object( grp ) )
		except:
			common.show_error("Failed to switch to editor", False)

	def create_new( self, event=None ):
		grp = rex.contacts.group( self.account, None )
		grp["id"] 		= "0"
		#grp["name"] 	= "New Group created on %s" % time.strftime( '%c' )
		grp["name"] 	= "Group"
		grp["id"]		= grp.save()
		# Now load the new group, pattern = 2 means by id
		cdom = u"<groups_get variable=\"id\" relation=\"=\" pattern=\"%s\" max=\"1\" page=\"1\" />" % \
				( grp["id"], )
		xmlstring = self.account.execute( 'base', "groups_get2", DOM=cdom )
		#try:
		xmlstring = common.uni2str( xmlstring )
		cdom 	= minidom.parseString( xmlstring )
		common.dprint ("Got XML ", xmlstring)
		gs		= cdom.firstChild
		cache 	= self.get_cache()
		for g in gs.getElementsByTagName("group"):
			grp = rex.contacts.group( self.account, g.toxml() )
			cache.append( grp )
			# Create a list item
			grp.list_index = self.AppendGroup( grp )
		#except Exception:
		self.resize_columns()
		self.currentItem = grp.list_index
		self.OnEditGroup( None )
		self.clear_cache = True

	def get_cache( self ):
		try:
			cache = self.groups[ self.current_page ]
		except KeyError:
			self.groups[ self.current_page ] = []
			cache = self.groups[ self.current_page ]
		return cache

	def get_selected( self ):
		sel 	= []
		count	= self.list.GetItemCount()
		if count < 1: return sel

		for i in range( 0, count ):
			if self.list.IsSelected( i ):
				sel.append( self.groups[ self.current_page ][i] )
		return sel

	def delete_selected( self, event=None ):
		# Confirm
		res = wx.MessageBox( "Are you sure you want to delete selected group(s)?", "Confirm Delete", wx.YES_NO )
		if res != wx.YES: return
		grps = self.get_selected()
		if len( grps ) < 1: return
		# Construct the DOM
		cdom = ""
		for grp in grps:
			cdom = cdom + ( "<g id=\"%s\" full=\"0\"/>" % grp["id"] )
		cdom = u"<gs>" + cdom + u"</gs>"
		try:
			res = self.account.execute( 'base', "groups_delete", DOM=cdom )
		except:
			common.show_error("Failed to delete")
			return
		if res:
			# Refresh this page
			self.GetPage( True )

	def expunge_selected( self, event=None ):
		# Confirm
		res = wx.MessageBox( "Are you sure you want to delete selected groups\n\nAND ALL MEMBERS ?\n\nThe selected groups and their contacts will be deleted forever!", "Confirm Full Delete", wx.YES_NO )
		if res != wx.YES: return
		grps = self.get_selected()
		if len( grps ) < 1: return
		# Construct the DOM
		cdom = ""
		for grp in grps:
			cdom = cdom + ( "<g id=\"%s\" full=\"1\" />" % grp["id"] )
		cdom = u"<gs>" + cdom + u"</gs>"
		try:
			res = self.account.execute( 'base', "groups_delete", DOM=cdom )
		except:
			common.show_error("Failed to delete")
			return
		if res:
			# Refresh this page
			self.GetPage( True )

	def for_contact( self, con ):
		self.qualifier	= "CONTACT"
		self.qid		= con["id"]
		self.rex_object = con

	#~ def for_pub_sub( self, pub ):
		#~ self.qualifier	= "PUBSUB"
		#~ self.qid		= pub["id"]

	#~ def for_pub_BLK( self, BLK ):
		#~ self.qualifier	= "PUBBLK"
		#~ self.qid		= pub["id"]

	def set_value( self, value ):
		pass
		#~ common.dprint( self.__class__, 'set value', value )

	def get_value( self ):
		pass
		#~ common.dprint( self.__class__, 'get value' )

	def set_readwrite( self, rw ):
		pass
		#~ common.dprint( self.__class__, 'set readwrite', rw )
		
		
####################################################################################################
		
class rq_control( wx.Panel ):
    def __init__( self, parent, account, main ):
        wx.Panel.__init__( self, parent, -1 )
        self.parent     = parent
        self.account     = account
        self.qualifier    = ""
        self.qid        = ""
        self.main        = main
        self.create_menu_items()
        # Connect a timer to this frame
        self.timer = wx.Timer( self, ID_CGTIMER_TIMER )
        self.Bind( wx.EVT_TIMER, self.OnTimer, id=ID_CGTIMER_TIMER )
        # Create the top bar
        self.topbar = wx.BoxSizer( wx.HORIZONTAL )
        # The refresh button
        self.refresh_button = wx.BitmapButton( self, -1, images.getRefreshBitmap(), size=common.button_size, style=wx.NO_BORDER )
        self.refresh_button.Bind( wx.EVT_BUTTON, self.OnRefresh )
        self.topbar.Add( self.refresh_button, 0 )
        # The show selector
        variables = ['', 'id','contact_id', 'source_id','target_id']
        self.variable_choice = wx.Choice( self, -1, choices=variables )
        self.variable_choice.SetSelection( 0 )
        self.variable_choice.Bind( wx.EVT_CHOICE, self.OnShowByChange )
        self.topbar.Add( self.variable_choice, 1, wx.EXPAND )
        # the relation button
        self.find_rel_button = wx.BitmapButton( self, -1, images.getEqualsBitmap(), size=common.button_size, style=wx.NO_BORDER )
        self.find_rel_button.Bind( wx.EVT_BUTTON, self.OnRelChange )
        self.find_rel_button.Enable( False )
        self.topbar.Add( self.find_rel_button, 0 )
        # The find text box
        self.find_text = wx.TextCtrl( self, -1, '', style=wx.TE_PROCESS_ENTER )
        self.find_text.Enable( False )
        self.find_text.Bind( wx.EVT_TEXT, self.OnFindTextKeyPress )
        self.find_text.Bind( wx.EVT_TEXT_ENTER, self.OnRefresh )
        self.topbar.Add( self.find_text, 2, wx.EXPAND )
        # The finding selector
        self.rpp_choice = wx.Choice( self, ID_RPP, choices=["10 per page", "30 per page","50 per page", "100 per page"] )
        self.rpp_choice.SetSelection( 1 )
        self.Bind( wx.EVT_CHOICE, self.OnRPPChange, id=ID_RPP )
        self.topbar.Add( self.rpp_choice, 1, wx.EXPAND )
        #self.find_by = 1    # Find by ID by default
        # zoom
        if main:
            self.zoom_button = wx.BitmapButton( self, -1, images.getZoomBitmap(), size=common.button_size, style=wx.NO_BORDER )
            self.zoom_button.Bind( wx.EVT_BUTTON, self.OnZoom )
            self.topbar.Add( self.zoom_button, 0 )
        # Enable the top bar controls
        self.reset_values()
        #self.enable_topbar( True )
        self.filter_set = False
        # Create the list
        self.list = wx.ListCtrl( self, ID_CONTACTS_LIST, style=wx.LC_REPORT )
        self.il = wx.ImageList(16, 16)
        # Load our images
        self.contact_icon     = self.il.Add( images.getContactBitmap() )
        self.list.SetImageList( self.il, wx.IMAGE_LIST_SMALL )
        # Store the default item color
        self.item_color = self.list.GetTextColour()
        self.list.Bind( wx.EVT_LEFT_DCLICK, self.OnItemDoubleClick )
        # default?
        self.list.Bind( wx.EVT_RIGHT_DOWN, self.OnListRightClick )
        # for wxGTK
        #self.list.Bind( wx.EVT_RIGHT_UP, self.OnListRightClick )
        # for wxMSW
        #self.list.Bind( wx.EVT_COMMAND_RIGHT_CLICK, self.OnListRightClick )
        self.Bind( wx.EVT_LIST_ITEM_SELECTED, self.OnItemActivated, id=ID_CONTACTS_LIST )
        self.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, id=ID_CONTACTS_LIST )
        # Set the drop target
        dt = drop_target( self, self.main )
        self.list.SetDropTarget( dt )
        # Add it's columns
        info = wx.ListItem()
        info.m_mask = wx.LIST_MASK_TEXT # | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
        info.m_format = 0
        info.m_text = "ID"
        self.list.InsertColumnInfo(0, info)

        info.m_text = "Source"
        self.list.InsertColumnInfo(1, info)

        info.m_text = "Target"
        self.list.InsertColumnInfo(2, info)

        info.m_text = "Requester"
        self.list.InsertColumnInfo(3, info)

        info.m_text = "Status"
        self.list.InsertColumnInfo(4, info)

        info.m_text = "Interface"
        self.list.InsertColumnInfo(5, info)

        info.m_text = "Reply Email"
        self.list.InsertColumnInfo(6, info)

        info.m_text = "CC Email"
        self.list.InsertColumnInfo(7, info)

        info.m_text = "BCC Email"
        self.list.InsertColumnInfo(8, info)

        info.m_text = "Created On"
        self.list.InsertColumnInfo(9, info)

        info.m_text = "Done On"
        self.list.InsertColumnInfo(10, info)

        info.m_text = "Pivs"
        self.list.InsertColumnInfo(11, info)
        
        # Resize them all
        self.resize_columns()

        # Create the bottom bar
        self.botbar = wx.BoxSizer( wx.HORIZONTAL )

        # Create prev and next buttons
        self.prev_button = wx.BitmapButton( self, -1, images.getLeftArrowBitmap(), style=wx.NO_BORDER )
        self.prev_button.Bind( wx.EVT_BUTTON, self.GetPrev )
        self.prev_button.Enable( False )
        self.botbar.Add( self.prev_button, 0, wx.EXPAND )
        self.pager = wx.StaticText( self, -1, ' ' )
        self.botbar.Add( self.pager, 1, wx.ALIGN_CENTER )
        self.next_button = wx.BitmapButton( self, -1, images.getRightArrowBitmap(), style=wx.NO_BORDER )
        self.next_button.Bind( wx.EVT_BUTTON, self.GetNext )
        self.botbar.Add( self.next_button, 0, wx.EXPAND )
        self.reset_values()
        # The whole mess
        self.sizer = wx.BoxSizer( wx.VERTICAL )
        self.sizer.Add( self.topbar, 0, wx.EXPAND )
        self.sizer.Add( self.list, 1, wx.EXPAND )
        self.sizer.Add( self.botbar, 0, wx.EXPAND )
        # Set the drag and drop events
        #if wx.Platform != '__WXMAC__':
        self.list.Bind( wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag )
        self.SetSizer( self.sizer )
        self.SetAutoLayout( 1 )
        if self.account.window: self.account.window.SetStatusText( "Ready" )
        wx.Yield()
        self.Layout()
        self.Show()

    def OnZoom( self, event ):
        if self.account.window: self.account.window.OnZoom( 4 )

    def OnRelChange( self, event ):
        self.filter_dirty = True
        if self.current_rel == '=':
            self.current_rel = '~'
            self.find_rel_button.SetBitmapLabel( images.getContainsBitmap() )
        elif self.current_rel == '~':
            self.current_rel = '!='
            self.find_rel_button.SetBitmapLabel( images.getNotEqualsBitmap() )
        elif self.current_rel == '!=':
            self.current_rel = '!~'
            self.find_rel_button.SetBitmapLabel( images.getNotContainsBitmap() )
        elif self.current_rel == '!~':
            self.current_rel = '='
            self.find_rel_button.SetBitmapLabel( images.getEqualsBitmap() )
        self.find_text.SetFocus()
        self.update_next_prev_buttons()

    def create_menu_items( self ):
        if self.main:
            menu = wx.Menu()
            menu.AppendSeparator()
            self.request_menu = menu
        else:
            menu = wx.Menu()
            menu.AppendSeparator()
            self.request_menu = menu

    def OnFindTextKeyPress( self, event ):
        self.filter_dirty = True
        self.current_pattern = self.find_text.GetValue()
        self.update_next_prev_buttons()
        common.dprint("current pattern now", self.current_pattern )

    def OnTimer( self, event ):
        self.GetPage()

    def OnBeginDrag( self, event ):
    	return
    def reset_values( self ):
        self.requests             = {}
        self.variable_choice.SetSelection( 0 )
        self.current_variable     = ''
        self.current_rel        = '='
        self.current_pattern    = ''
        self.current_max        = 30
        self.current_page        = 0
        self.total_pages        = 0
        self.total_requests        = 0
        self.filter_dirty         = False

    def resize_columns( self ):
        '''Used internally to recalculate the column sizes'''
        # Set the column header sizes
        for i in range( self.list.GetColumnCount() ):
            self.list.SetColumnWidth( i, wx.LIST_AUTOSIZE )

    def OnShowByChange( self, event ):
        variable = self.variable_choice.GetStringSelection()
        variable = variable.strip()
        if variable == self.current_variable: return
        self.filter_dirty = True
        self.current_variable = variable
        self.find_text.Enable( self.current_variable != '' )
        self.find_rel_button.Enable( self.current_variable != '' )
        if self.current_variable != '':
            self.find_text.SetFocus()
        self.update_next_prev_buttons()

    def OnRPPChange( self, event ):
        sel = self.rpp_choice.GetSelection()
        if sel == 0 :
            self.current_max = 10
        elif sel == 1 :
            self.current_max = 30
        elif sel == 2 :
            self.current_max = 50
        elif sel == 3 :
            self.current_max = 100
        else:
            self.current_max = 10

    def AppendRequest( self, req ):
        x = self.list.GetItemCount()
        id = req["id"]
        x = self.list.InsertImageStringItem( sys.maxint, id, self.contact_icon )
        source = req["source_type"] + " " + req["source_id"]
        self.list.SetStringItem( x , 1, source )
        target = req["target_type"] + " " + req["target_id"]
        self.list.SetStringItem( x , 2, target )
        self.list.SetStringItem( x , 3, req["requester"] )
        self.list.SetStringItem( x , 4, req["status"] )
        self.list.SetStringItem( x , 5, req["interface"] )
        self.list.SetStringItem( x , 6, req['reply_email'] )
        self.list.SetStringItem( x , 7, req['cc_email'] )
        self.list.SetStringItem( x , 8, req['bcc_email'] )
        self.list.SetStringItem( x , 9, req['created_on'] )
        self.list.SetStringItem( x , 10,req["done_on"] )
        self.list.SetStringItem( x , 11,req["pivs"] )
        self.list.SetItemData( x, x )
        req.list_item = self.list.GetItem( x )
        return x

    def OnRefresh( self, event=None ):
        self.GetPage( True )

    def GetPage( self, refresh=False ):
        # Set the filter attibs,
        self.list.DeleteAllItems()
        if self.filter_dirty:
            self.requests        = {}
            self.filter_dirty    = False
            self.current_page    = 1
        elif refresh:
            try: del self.requests[ self.current_page ]
            except: pass
            if self.current_page == 0: self.current_page = 1

        # Update the status bar
        if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
        wx.Yield()
        wx.Yield()
        wx.Yield()
        # Do we use the cache or download ?
        try:
            cache = self.requests[ self.current_page ]
        except KeyError:
            cache = None

        if cache:
            for req in cache:
                req.list_index = self.AppendRequest( req )
        else:
            # Make the call
            group_id = ''
            if not self.main:
                group_id = " group_id=\"%s\" " % self.rex_object['id']
            rdom = "<requests_get variable=\"%s\" relation=\"%s\" pattern=\"%s\" max=\"%s\" page=\"%s\" %s />" % \
                    ( self.current_variable, self.current_rel, self.current_pattern, self.current_max, self.current_page, group_id )
            cur = wx.BusyCursor()
            rqts = None
            try:
                xmlstring = self.account.execute( 'base', "requests_get", DOM=rdom )
                #xmlstring = common.uni2str( xmlstring )
                self.account.window.SetStatusText( "Received data" )
                xmlstring = common.prepare_xml( xmlstring )
                rdom     = minidom.parseString( xmlstring )
                rqts    = rdom.firstChild
            except:
                if self.account.window: self.account.window.SetStatusText( "Failed" )
            del cur
            if not rqts: return
            self.total_pages     = int( rqts.attributes["pages"].value )
            self.total_requests = int( rqts.attributes["count"].value )
            self.account.window.SetStatusText( "Total records: " + rqts.attributes["count"].value )
            self.requests[ self.current_page ] = []
            cache = self.requests[ self.current_page ]
            for rqt in rqts.getElementsByTagName("request"):
                req = None
                try:
                    req = rex.contacts.request( self.account, rqt.toxml() )
                except:
                    common.show_error( "failed to parse request [[%s]]" % rqt.toxml().encode( 'utf-8' ), False )
                if not req: continue
                cache.append( req )
                # Create a list item
                req.list_index = self.AppendRequest( req )
            #except Exception:
            #    print "Failed to load account contacts."
        self.resize_columns()
        label = "   Page %s of %s [%s total]   " % (self.current_page, self.total_pages, self.total_requests)
        #print "Label ", label
        self.pager.SetLabel( label )
        self.update_next_prev_buttons()
        if self.account.window: self.account.window.SetStatusText( "Ready" )
        wx.Yield()


    def GetNext( self, event ):
        # if self.current_page >= self.total_pages: return
        self.timer.Stop()
        self.current_page = self.current_page + 1
        # override?
        if self.filter_dirty:
            self.requests        = {}
            self.filter_dirty    = False
            self.current_page    = 1

        if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
        #wx.Yield()
        # Cache exists already?
        try:
            cache = self.requests[ self.current_page ]
            self.GetPage()
            return
        except KeyError:
            pass
        self.timer.Start( 700, 1 )

    def GetPrev( self, event ):
        if self.current_page <= 1: return
        self.timer.Stop()
        self.current_page = self.current_page - 1
        if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
        #wx.Yield()
        # Cache exists already?
        try:
            cache = self.requests[ self.current_page ]
            self.GetPage()
            return
        except KeyError:
            pass
        self.timer.Start( 700, 1 )

    def update_next_prev_buttons( self ):
        #print "Checking np %s, %s" % ( self.current_page, self.total_pages )
        if self.filter_dirty:
            self.next_button.Enable( True )
            self.prev_button.Enable( False )
            return
        #
        if self.current_page >= self.total_pages:
            #print "DISABLING next_button"
            self.next_button.Enable( False )
        else:
            #print "ENABLING next_button"
            self.next_button.Enable( True )

        if self.current_page <= 1:
            #print "DISABLING prev_button"
            self.prev_button.Enable( False )
        else:
            #print "ENABLING prev_button"
            self.prev_button.Enable( True )

    def OnListRightClick( self, event ):
        try:
            point = event.GetPositionTuple()
            if wx.Platform == '__WXGTK__':
                point = wx.Point( event.m_x + 5, event.m_y + 45 )
            self.PopupMenu( self.contact_menu, point )
        except:
            pass

    def OnItemDoubleClick( self, event ):
        self.OnEditRequest( event )
        return

    def OnItemActivated( self, event ):
        self.currentItem = event.m_itemIndex

    def OnEditRequest( self, event ):
        req = self.requests[self.current_page][self.currentItem]
        if req is None: return
        #~ print "Editing contact %s" % con["first_name"]
        if req.editor is not None:
            self.account.window.ebook.SetSelection( common.get_page_index( self.account.window.ebook, req.editor ) )
            return
        req.editor = contacts.request( self.account, req, self.currentItem )
        # add the page
        self.account.window.ebook.AddPage( req.editor, req.editor.get_label_text() )
        pi = common.get_page_index( self.account.window.ebook, req.editor )
        try:
            self.account.window.ebook.SetSelection( pi )
            self.account.window.ebook.SetPageImage( pi, self.account.window.icon_from_rex_object( req ) )
        except:
            common.show_error("Failed to switch to editor", False)

    def get_cache( self ):
        try:
            cache = self.requests[ self.current_page ]
        except KeyError:
            self.requests[ self.current_page ] = []
            cache = self.requests[ self.current_page ]
        return cache

    def get_selected( self ):
        sel = []
        count = self.list.GetItemCount()
        if count < 1: return sel
        for i in range( 0, count ):
            if self.list.IsSelected( i ):
                sel.append( self.requests[ self.current_page ][i] )
        return sel
            
    def set_value( self, value ):
        pass
        #~ common.dprint( self.__class__, 'set value', value )

    def get_value( self ):
        pass
        #~ common.dprint( self.__class__, 'get value' )

    def set_readwrite( self, rw ):
        pass
        #~ common.dprint( self.__class__, 'set readwrite', rw )

####################################################################################################


class pq_control( wx.Panel ):
    def __init__( self, parent, account, main ):
        wx.Panel.__init__( self, parent, -1 )
        self.parent     = parent
        self.account     = account
        self.qualifier    = ""
        self.qid        = ""
        self.main        = main
        self.create_menu_items()
        self.initial_load = True
        # Connect a timer to this frame
        self.timer = wx.Timer( self, ID_CGTIMER_TIMER )
        self.Bind( wx.EVT_TIMER, self.OnTimer, id=ID_CGTIMER_TIMER )
        # Create the top bar
        self.topbar = wx.BoxSizer( wx.HORIZONTAL )
        # The refresh button
        self.refresh_button = wx.BitmapButton( self, -1, images.getRefreshBitmap(), size=common.button_size, style=wx.NO_BORDER )
        self.refresh_button.Bind( wx.EVT_BUTTON, self.OnRefresh )
        self.topbar.Add( self.refresh_button, 0 )
        # The show selector
        variables = ['', 'id','request_id','contact_id', 'created_on']
        self.variable_choice = wx.Choice( self, -1, choices=variables )
        self.variable_choice.SetSelection( 0 )
        self.variable_choice.Bind( wx.EVT_CHOICE, self.OnShowByChange )
        self.topbar.Add( self.variable_choice, 1, wx.EXPAND )
        # the relation button
        self.find_rel_button = wx.BitmapButton( self, -1, images.getEqualsBitmap(), size=common.button_size, style=wx.NO_BORDER )
        self.find_rel_button.Bind( wx.EVT_BUTTON, self.OnRelChange )
        self.find_rel_button.Enable( False )
        self.topbar.Add( self.find_rel_button, 0 )
        # The find text box
        self.find_text = wx.TextCtrl( self, -1, '', style=wx.TE_PROCESS_ENTER )
        self.find_text.Enable( False )
        self.find_text.Bind( wx.EVT_TEXT, self.OnFindTextKeyPress )
        self.find_text.Bind( wx.EVT_TEXT_ENTER, self.OnRefresh )
        self.topbar.Add( self.find_text, 2, wx.EXPAND )
        # The finding selector
        self.rpp_choice = wx.Choice( self, ID_RPP, choices=["10 per page", "30 per page","50 per page", "100 per page"] )
        self.rpp_choice.SetSelection( 1 )
        self.Bind( wx.EVT_CHOICE, self.OnRPPChange, id=ID_RPP )
        self.topbar.Add( self.rpp_choice, 1, wx.EXPAND )
        #self.find_by = 1    # Find by ID by default
        # zoom
        if main:
            self.zoom_button = wx.BitmapButton( self, -1, images.getZoomBitmap(), size=common.button_size, style=wx.NO_BORDER )
            self.zoom_button.Bind( wx.EVT_BUTTON, self.OnZoom )
            self.topbar.Add( self.zoom_button, 0 )
        # Enable the top bar controls
        self.reset_values()
        #self.enable_topbar( True )
        self.filter_set = False
        # Create the list
        self.list = wx.ListCtrl( self, ID_CONTACTS_LIST, style=wx.LC_REPORT )
        self.il = wx.ImageList(16, 16)
        # Load our images
        self.contact_icon     = self.il.Add( images.getContactBitmap() )
        self.list.SetImageList( self.il, wx.IMAGE_LIST_SMALL )
        # Store the default item color
        self.item_color = self.list.GetTextColour()
        self.list.Bind( wx.EVT_LEFT_DCLICK, self.OnItemDoubleClick )
        # default?
        self.list.Bind( wx.EVT_RIGHT_DOWN, self.OnListRightClick )
        # for wxGTK
        #self.list.Bind( wx.EVT_RIGHT_UP, self.OnListRightClick )
        # for wxMSW
        #self.list.Bind( wx.EVT_COMMAND_RIGHT_CLICK, self.OnListRightClick )
        self.Bind( wx.EVT_LIST_ITEM_SELECTED, self.OnItemActivated, id=ID_CONTACTS_LIST )
        self.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, id=ID_CONTACTS_LIST )
        # Set the drop target
        dt = drop_target( self, self.main )
        self.list.SetDropTarget( dt )
        # Add it's columns
        info = wx.ListItem()
        info.m_mask = wx.LIST_MASK_TEXT # | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
        info.m_format = 0
        info.m_text = "ID"
        self.list.InsertColumnInfo(0, info)

        info.m_text = "Request ID"
        self.list.InsertColumnInfo(1, info)

        info.m_text = "Status"
        self.list.InsertColumnInfo(2, info)

        info.m_text = "Contact ID"
        self.list.InsertColumnInfo(3, info)

        info.m_text = "Created on"
        self.list.InsertColumnInfo(4, info)
        
        # Resize them all
        self.resize_columns()

        # Create the bottom bar
        self.botbar = wx.BoxSizer( wx.HORIZONTAL )

        # Create prev and next buttons
        self.prev_button = wx.BitmapButton( self, -1, images.getLeftArrowBitmap(), style=wx.NO_BORDER )
        self.prev_button.Bind( wx.EVT_BUTTON, self.GetPrev )
        self.prev_button.Enable( False )
        self.botbar.Add( self.prev_button, 0, wx.EXPAND )
        self.pager = wx.StaticText( self, -1, ' ' )
        self.botbar.Add( self.pager, 1, wx.ALIGN_CENTER )
        self.next_button = wx.BitmapButton( self, -1, images.getRightArrowBitmap(), style=wx.NO_BORDER )
        self.next_button.Bind( wx.EVT_BUTTON, self.GetNext )
        self.botbar.Add( self.next_button, 0, wx.EXPAND )
        self.reset_values()
        # The whole mess
        self.sizer = wx.BoxSizer( wx.VERTICAL )
        self.sizer.Add( self.topbar, 0, wx.EXPAND )
        self.sizer.Add( self.list, 1, wx.EXPAND )
        self.sizer.Add( self.botbar, 0, wx.EXPAND )
        # Set the drag and drop events
        #if wx.Platform != '__WXMAC__':
        self.list.Bind( wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag )
        self.SetSizer( self.sizer )
        self.SetAutoLayout( 1 )
        if self.account.window: self.account.window.SetStatusText( "Ready" )
        wx.Yield()
        self.Layout()
        self.Show()
        self.current_page = 1

    def OnZoom( self, event ):
        if self.account.window: self.account.window.OnZoom( 4 )

    def OnRelChange( self, event ):
        self.filter_dirty = True
        if self.current_rel == '=':
            self.current_rel = '~'
            self.find_rel_button.SetBitmapLabel( images.getContainsBitmap() )
        elif self.current_rel == '~':
            self.current_rel = '!='
            self.find_rel_button.SetBitmapLabel( images.getNotEqualsBitmap() )
        elif self.current_rel == '!=':
            self.current_rel = '!~'
            self.find_rel_button.SetBitmapLabel( images.getNotContainsBitmap() )
        elif self.current_rel == '!~':
            self.current_rel = '='
            self.find_rel_button.SetBitmapLabel( images.getEqualsBitmap() )
        self.find_text.SetFocus()
        self.update_next_prev_buttons()

    def create_menu_items( self ):
        if self.main:
            menu = wx.Menu()
            menu.AppendSeparator()
            self.request_menu = menu
        else:
            menu = wx.Menu()
            menu.AppendSeparator()
            self.request_menu = menu

    def OnFindTextKeyPress( self, event ):
        self.filter_dirty = True
        self.current_pattern = self.find_text.GetValue()
        self.update_next_prev_buttons()
        common.dprint("current pattern now", self.current_pattern )

    def OnTimer( self, event ):
        self.GetPage()

    def OnBeginDrag( self, event ):
    	return
    def reset_values( self ):
        self.pq             = {}
        self.variable_choice.SetSelection( 0 )
        self.specific_request = 0
        self.initial_load = True
        self.current_variable     = ''
        self.current_rel        = '='
        self.current_pattern    = ''
        self.current_max        = 30
        self.current_page        = 0
        self.total_pages        = 0
        self.total_requests        = 0
        self.filter_dirty         = False
        
    def for_request( self, request ):
		self.specific_request = request["id"]
		self.rex_object = request

    def resize_columns( self ):
        '''Used internally to recalculate the column sizes'''
        # Set the column header sizes
        for i in range( self.list.GetColumnCount() ):
            self.list.SetColumnWidth( i, wx.LIST_AUTOSIZE )

    def OnShowByChange( self, event ):
        variable = self.variable_choice.GetStringSelection()
        variable = variable.strip()
        if variable == self.current_variable: return
        self.filter_dirty = True
        self.current_variable = variable
        self.find_text.Enable( self.current_variable != '' )
        self.find_rel_button.Enable( self.current_variable != '' )
        if self.current_variable != '':
            self.find_text.SetFocus()
        self.update_next_prev_buttons()

    def OnRPPChange( self, event ):
        sel = self.rpp_choice.GetSelection()
        if sel == 0 :
            self.current_max = 10
        elif sel == 1 :
            self.current_max = 30
        elif sel == 2 :
            self.current_max = 50
        elif sel == 3 :
            self.current_max = 100
        else:
            self.current_max = 10

    def AppendPQEntry( self, req ):
        x = self.list.GetItemCount()
        id = req["id"]
        x = self.list.InsertImageStringItem( sys.maxint, id, self.contact_icon )
        self.list.SetStringItem( x , 1, req["request_id"] )
        self.list.SetStringItem( x , 2, req["status"] )
        self.list.SetStringItem( x , 3, req["contact_id"] )
        self.list.SetStringItem( x , 4, req["created_on"] )
        self.list.SetItemData( x, x )
        req.list_item = self.list.GetItem( x )
        return x

    def OnRefresh( self, event=None ):
        self.GetPage( True )

    def GetPage( self, refresh=False, searchfor = 0 ):
    	#first, are we jumping to a specific entry?
    	jump_to_specific = False
    	if searchfor > 0 or (self.initial_load and self.specific_request != 0) :
    		self.initial_load = False
    		jump_to_specific = True
    		searchfor = self.specific_request
    	
        # Set the filter attibs,
        self.list.DeleteAllItems()
        if self.filter_dirty:
            self.pq        = {}
            self.filter_dirty    = False
            self.current_page    = 1
        elif refresh:
            try: del self.pq[ self.current_page ]
            except: pass
            if self.current_page == 0: self.current_page = 1

        # Update the status bar
        if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
        wx.Yield()
        wx.Yield()
        wx.Yield()
        # Do we use the cache or download ?
        try:
            cache = self.pq[ self.current_page ]
        except KeyError:
            cache = None

        if cache:
            for req in cache:
                req.list_index = self.AppendPQEntry( req )
        else:
            # Make the call
            group_id = ''
            rdom = "<pq_get variable=\"%s\" relation=\"%s\" pattern=\"%s\" max=\"%s\" page=\"%s\" pagewith=\"%s\" where=\"\"/>" % \
                    ( self.current_variable, self.current_rel, self.current_pattern, self.current_max, self.current_page, searchfor )
            cur = wx.BusyCursor()
            rqts = None
            try:
                xmlstring = self.account.execute( 'base', "pq_get", DOM=rdom )
                #xmlstring = common.uni2str( xmlstring )
                self.account.window.SetStatusText( "Received data" )
                xmlstring = common.prepare_xml( xmlstring )
                rdom     = minidom.parseString( xmlstring )
                rqts    = rdom.firstChild
            except:
                if self.account.window: self.account.window.SetStatusText( "Failed" )
            del cur
            if not rqts: return
            self.total_pages     = int( rqts.attributes["pages"].value )
            self.total_pq = int( rqts.attributes["count"].value )
            self.current_page = int( rqts.attributes["page"].value )
            self.account.window.SetStatusText( "Total records: " + rqts.attributes["count"].value )
            self.pq[ self.current_page ] = []
            cache = self.pq[ self.current_page ]
            correct_page = False
            for rqt in rqts.getElementsByTagName("pq_entry"):
              	req = None
               	try:
                  	req = rex.contacts.pq_entry( self.account, rqt.toxml() )
                  	
                except:
                   	common.show_error( "failed to parse request [[%s]]" % rqt.toxml().encode( 'utf-8' ), False )
                if not req: continue
                cache.append( req )
             	# Create a list item
               	req.list_index = self.AppendPQEntry( req )
            	   		
        self.resize_columns()
        label = "   Page %s of %s [%s total]   " % (self.current_page, self.total_pages, self.total_pq)
        self.pager.SetLabel( label )
        self.update_next_prev_buttons()
        #if self.account.window: self.account.window.SetStatusText( "Ready" )
        wx.Yield()


    def GetNext( self, event ):
        # if self.current_page >= self.total_pages: return
        self.timer.Stop()
        self.current_page = self.current_page + 1
        # override?
        if self.filter_dirty:
            self.pq        = {}
            self.filter_dirty    = False
            self.current_page    = 1

        if self.account.window: 
         	self.account.window.SetStatusText( "Getting page " +str( self.current_page ) + " ... " )
        #wx.Yield()
        # Cache exists already?
        try:
            cache = self.pq[ self.current_page ]
            self.GetPage( )
            return
        except KeyError:
            pass
        self.timer.Start( 700, 1 )

    def GetPrev( self, event ):
        if self.current_page <= 1: return
        self.timer.Stop()
        self.current_page = self.current_page - 1
        if self.account.window: self.account.window.SetStatusText( "Getting page %s ..." % self.current_page )
        #wx.Yield()
        # Cache exists already?
        try:
            cache = self.pq[ self.current_page ]
            self.GetPage()
            return
        except KeyError:
            pass
        self.timer.Start( 700, 1 )

    def update_next_prev_buttons( self ):
        #print "Checking np %s, %s" % ( self.current_page, self.total_pages )
        if self.filter_dirty:
            self.next_button.Enable( True )
            self.prev_button.Enable( False )
            return
        #
        if self.current_page >= self.total_pages:
            #print "DISABLING next_button"
            self.next_button.Enable( False )
        else:
            #print "ENABLING next_button"
            self.next_button.Enable( True )

        if self.current_page <= 1:
            #print "DISABLING prev_button"
            self.prev_button.Enable( False )
        else:
            #print "ENABLING prev_button"
            self.prev_button.Enable( True )

    def OnListRightClick( self, event ):
        try:
            point = event.GetPositionTuple()
            if wx.Platform == '__WXGTK__':
                point = wx.Point( event.m_x + 5, event.m_y + 45 )
            self.PopupMenu( self.contact_menu, point )
        except:
            pass

    def OnItemDoubleClick( self, event ):
        #self.OnEditRequest( event )
        return

    def OnItemActivated( self, event ):
        self.currentItem = event.m_itemIndex

    def OnEditRequest( self, event ):
        req = self.pq[self.current_page][self.currentItem]
        if req is None: return
        #~ print "Editing contact %s" % con["first_name"]
        if req.editor is not None:
            self.account.window.ebook.SetSelection( common.get_page_index( self.account.window.ebook, req.editor ) )
            return
        req.editor = contacts.contact( self.account, req, self.currentItem )
        # add the page
        self.account.window.ebook.AddPage( req.editor, req.editor.get_label_text() )
        pi = common.get_page_index( self.account.window.ebook, req.editor )
        try:
            self.account.window.ebook.SetSelection( pi )
            self.account.window.ebook.SetPageImage( pi, self.account.window.icon_from_rex_object( req ) )
        except:
            common.show_error("Failed to switch to editor", False)

    def get_cache( self ):
        try:
            cache = self.pq[ self.current_page ]
        except KeyError:
            self.pq[ self.current_page ] = []
            cache = self.pq[ self.current_page ]
        return cache

    def get_selected( self ):
        sel = []
        count = self.list.GetItemCount()
        if count < 1: return sel
        for i in range( 0, count ):
            if self.list.IsSelected( i ):
                sel.append( self.pq[ self.current_page ][i] )
        return sel
            
    def set_value( self, value ):
        pass
        #~ common.dprint( self.__class__, 'set value', value )

    def get_value( self ):
        pass
        #~ common.dprint( self.__class__, 'get value' )

    def set_readwrite( self, rw ):
        pass
        #~ common.dprint( self.__class__, 'set readwrite', rw )

####################################################################################################
